#ifndef CLASSE_H
#define CLASSE_H

#include <iostream>
#include <vector>
#include <map>
#include <set>

#include "debugstreams.h"
#include "lib.h"
#include "Sort.h"
#include "Prerequis.h"
#include "ListComp.h"
#include "ListCapa.h"

#include <boost/assign/list_of.hpp>

using namespace std;

/**
 * La classe Classe représente une classe au sens AD&D. C'est une
 * classe abstraite, qui sert au polymorphisme. Ici se fait la gestion
 * des diverses restrictions et compétences, qui sont lues depuis
 * chaque type de classe. L'objet Classe contient tous les attributs
 * et accesseurs communs aux différents types de classes.
 *
 * On ne stocke ici que des pointeurs sur les listes (et nombre) de
 * compétences et restrictions.
 *
 * @author Johan "Solo" Luisier
 */
class Classe
{
 private:
  /**
   * Sert à régler les problèmes de libération de mémoire ?!?
   */
  static const unsigned int Zero;
 protected:
  /**
   * Nom des traits, utilisés pour l'affichage. L'attribut static est
   * utilisé par souci d'optimisation.
   */
  static const string Caracteristiques[6];
  /*
   * Niveau, niveau magique et nombre de pts d'expérience.
   */
  unsigned int
    Niveau, /**< Niveau dans cette classe, calculé automatiquement. */
    Niveau_mag, /**< Niveau de magie dans cette classe, calculé
		   automatiquement. */
    XP; /**< Nombre de pt d'expérience dans cette classe. */
  /**
   * Valeurs des traits du personnage, le 7ème contient le score de
   * force exceptionnel (0 si non applicable).
   */
  unsigned int Traits[7];
  /**
   * Nom de la classe ("Rôdeur", "Barde", ...).
   */
  string Type_classe;
  /**
   * Nom de la Race, utilisé lors de la détermination des scores et
   * des traits.
   */
  string Type_race;
  /**
   * Liste des compétences que l'objet courant possède. Il s'agit de
   * la liste des \b numéros des compétences dans la liste de
   * l'instance. Si une compétences est associée à un pourcentage, ce
   * pourcentage est également stocké ici.
   */
  vector<unsigned int> Comp_classe;
  /**
   * Liste des sorts du livre de sort (Mages, Spécialistes et Bardes
   * uniquement).
   */
  vector<Sort> Cl_sorts;
  /**
   * Type de classe (TypeClasse) disponibles, détermine les
   * compétences non-martiales accessibles. Est déterminé d'après la
   * classe dérivée lors de la création de l'objet.
   *
   * @see TypeClasse
   */
  vector<TypeClasse> TC;
  /**
   * Niveau de sortie des fonctions globales, très simples. On peut
   * changer cette constante en modifiant la ligne Classe::elementaire
   * du fichier CONFIG.
   */
  static DebugLevelType elementaire;
  /**
   * Niveau de sortie des fonctions plus spécifiques de Classe. La
   * valeur de cette constante est définie par la ligne
   * Classe::fonction du fichier CONFIG.
   */
  static DebugLevelType fonction;
  const vector<ListComp>
    *ptrComp, /**< Pointeur sur les compétences de classe */
    *ptrRes;/**< Pointeur sur les restrictions */
  const unsigned int
    *ptrNbrComp, /**< Pointeur sur le nombre de compétences */
    *ptrNbrRes,  /**< Pointeur sur le nombre de restrictions */
    *ptrNbrEns,  /**< Pointeur sur le nombre d'ensembles de compétences */
    *ptrMaxEns;  /**< Pointeur sur le nombre max d'élément d'un ensemble */
  /**
   * Pointe sur les ensembles de compétences. On utilise ici la classe
   * ListCapa, afin de pouvoir associer un éventuel coût à l'ensemble
   * (ce qui permet alors de créer un ensemble "Clerc standard",
   * "Druide standard", ...).
   */
  const vector<ListCapa> *ptrNomEnsComp;
  /**
   * Contient les prérequis relatifs aux traits pour la Classe donnée.
   */
  const vector< Prerequis > *ptrTraitsRequis;
  /**
   * Pointeur sur l'ensemble des Races pouvant être de cette Classe.
   */
  const set< string > *ptrRacesAdmises;
  /**
   * Calcule le niveau du personnage, en fonction de sa classe et de son
   * nombre de pts d'expérience.
   */
  virtual void calculNiveau() = 0;
  /**
   * Calcule le TAC0 du personnage
   */
  virtual unsigned int calculeTAc0() const = 0;
  /**
   * Calcule le niveau de magie du personnage, en fonction de sa classe
   * et de son niveau (recalculé par précaution). Si le personnage n'a pas
   * de sorts ou a un niveau insuffisant pour en lancer, le niveau magique
   * calculé est 0.
   */
  virtual void calculNivMag() = 0;
 public:
  Classe();
  Classe(unsigned int xp, unsigned int trait[7], TypeClasse tc,
	 const string& race);
  Classe(const Classe& classe);
  virtual ~Classe();
  void setCompetences(const vector<unsigned int> capa);
  void affiche() const;
  unsigned int calculeNombres();
  void afficherStats(ostream& os = blank());
  void afficheCompetences(ostream& os = blank()) const;
  string getClasse() const;
  unsigned int getXP() const;
  void setTrait(const unsigned int& i, const unsigned int& val);
  unsigned int getTrait(const unsigned int& i) const;
  unsigned int getCombien() const;
  unsigned int getComp(const unsigned int& i) const;
  virtual void appliquerModif();
  virtual void augmenterTalents(const bool& lvl_up);
  /**
   * Calcule les JS du personnage et les mets dans le tableau passé en
   * argument. Si le personnage a plusieurs classes, les JS les plus
   * avantageux seront automatiquement sélectionnés dans la classe
   * Joueur.
   *
   * @param tab pointeur sur le tableau contenant les JS.
   */
  virtual void getJS(unsigned int *tab) const = 0;
  string getCompetence(const unsigned int& i, bool& sauter,
		       const bool& LaTeX = false) const;
  virtual unsigned int getValeurBase(const unsigned int& i) const;
  unsigned int getNiveau() const;
  unsigned int getNiveauMagie() const;
  virtual void ecrireSorts(const string& nom_fichier, const bool& ecrase) const;
  virtual void nbrSort(unsigned int sorts[], const unsigned int& taille) const;
  string supprimeAccents(const string& brut) const;
  const vector<TypeClasse>* getTC() const;
  void augmenterXP(const unsigned int& xp);
  static void setLevel(const DebugLevelType& level, const unsigned int& n);
  virtual void ajoutSorts();
  void ajoutSorts(const vector<Sort>& vec);
  void setSorts(const vector<Sort>& sorts);
  void setXP(const unsigned int& xp);
  unsigned int getCombienSorts() const;
  Sort getSort(const unsigned int& i) const;
  const vector<Sort>* getSortAdresse() const;
  void getCompetences(unsigned int& nbrComp, vector<ListComp>& comp, unsigned int& nbrRes, vector<ListComp>& restric) const;
  virtual void getEnsemblesCompetences(unsigned int& nbr, unsigned int& max) const;
  virtual const vector<unsigned int>* getEnsembleCompetences(const unsigned int& nbr) const;
  /* const string* getNomEnsemble(const unsigned int& nbr) const; */
  /* const unsigned int* getPrixEnsemble(const unsigned int& nbr) const; */
  const ListCapa* getEnsemble(const unsigned int& nbr) const;
  const vector<ListCapa>* getEnsembles() const;
  unsigned int getPP() const;
  /**
   * Fait correspondre le prix d'accès à une compétence martiale en
   * fonction de la classe du personnage.
   *
   * @param Map pointeur sur la map qui sera la table de
   * correspondance.
   */
  virtual void getPrixAcces(map<string, unsigned int>*& Map) const = 0;
  const vector<unsigned int>* getToutesCompetences(unsigned int& nbr) const;
  ListComp getCompetenceNumero(const unsigned int& i) const;
  bool possedePourcent() const;
  virtual unsigned int getPourcent(const bool& lvlUp) const;
  virtual bool forceExceptionnelle() const;
  virtual string getScoreAdditionnel(const unsigned int& i) const;
  void construitClasse(unsigned int& pps);
  const unsigned int* getNbrEns() const;
  const unsigned int* getMaxEns() const;
  virtual StatusCode prerequisOK(const string& race,
				 const unsigned int& ensemble = 99,
				 const bool& complexe = false);
};

#endif
